最近在工作中发现一个有意思的现象,我用 ctrl+c 关闭本地 consul 的时候,报警系统并没有发出告警,说我的 node 异常,自己看了一下代码,发现 consul 的关闭还是有点猫腻的,仔细来讲讲
consul agent 在正常关闭的时候会向集群发送 leave 信令,宣告自己离开集群,那么什么才叫正常关闭呢?
还是上代码:
摘自command.go handleSignals方法
|
|
首先 agent 监听了三个系统信令,os.Interrupt, syscall.SIGTERM, syscall.SIGHUP
看26行,syscall.SIGHUP,用于 reloadconfig, 这个忽略掉
接下来才是进入正题,如何正确的关闭 consul
首先我们要知道 consul agent 关闭后 consul server 一般是什么体现.
- consul ui 显示 Node 异常, check 显示是 fialling
- consul ui 中搜索不到这个 node
是什么造成这个差别呢,答案在上面代码里面的第49行到55行,正常关闭后, node 会向 server 发送一条 leave 的信令,告诉 server 我离开了,你不用管我了,但是如果是宕机的话,又不能发送这个 leave 信令,让 server 知道我falling 了,这里通过34行定义的graceful来处理,如果配置里面说需要发送 leave 信令,那么就发送一个.
这里说一下, consul 的 service 或者 node 状态监控可以通过consul_alert这个项目来做(这里吐个槽,虽然我在用这个程序做报警,但是真心觉得这个代码写得不敢恭维,不停的 exet 子进程,不停的初始化,想要写alert 扩展的,一定要考虑重复初始化到知道内存泄漏)
原理说完,槽吐完,接下来说点好的,这也是我觉得 consul 开发者很贴心的一个地方.
作者把os.Interrupt, syscall.SIGTERM两个信令分开处理,
os.Interrupt
这个信令对应的其实就是 ctrl+c, 这一般是我们在开发时才会用到,那么和这个信令配合的配置是config.SkipLeaveOnInt ,这个配置项不配置,默认就是 false, 那么`sig == os.Interrupt && !(config.SkipLeaveOnInt)` 就为 true, 接着就会执行 leave信令,这完全适用于我们在开发环境中,使用自己开发电脑上的 consul agent, 退出就自动注销,不用怕收到报警.
syscall.SIGTERM
这个信令一般我们执行
kill -15 ${pid}
就会发送,当然kill ${pid}
默认就是发送15号信令,这个信令配合config.LeaveOnTerm配置项来处理是否发送 leave 信令,这种一般是 agent 在后台运行时才会用到的,这种情况大家都知道主要场景是在生产环境,如果配置config.LeaveOnTerm= true 的话,那么停机维护的时候,也收不到烦人的报警.贴心吧
有人说,宕机怎么办,我很遗憾的告诉你 os.KILL 信令,程序接收到也没有时间执行下面的操作,直接被杀掉了,在服务器宕机的时候也许都没机会发出 kill 命令,在程序被 kernel 杀掉也就是 kill -9,当然程序也没办法执行下面的操作,当然在 server 端看到的状态就是 falling,接收收到报警,找运维人员处理就好了.
最后总结一下,关闭 consul 的正确姿势:
在前台运行的情况下:
ctrl+c +最简化配置即可正常关闭,
在后台运行的情况下:
配置中指定
LeaveOnTerm: true
,维护时,使用kill -15 ${pid}
来关闭进程,即可正常关闭掉 node, 并注销成功.
补充说明:
leave 的信令发送时异步发送的,所以如果网络不太好的情况下,也许会 leave 还没完全发出去程序就关闭掉了.代码见上面49行,我没想明白为什么要一步处理,同步处理,就算发送失败重试三次再管也好的….